#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../inc/assistant.h"
#include "../inc/defs.h"
#include "../inc/command.h"
#include "../inc/log.h"
#include "../inc/parse.h"

/* Constantes simbolicas */
#define MAX_LENGTH_PARAM	50
#define MAX_BUFFER			512
#define MAX_COMMAND			11

/* Funciones privadas */
static void shellCommand(commandADT command);
static int parseArgCommandLine(int argc, char **argv);

/* Prototipo de funciones wrappers */

static int InsertAirport_W(int cantParam, char **param);
static int InsertFlight_W(int cantParam, char **param);
static int MassiveAirInsert_W(int cantParam, char **param);
static int MassiveFlightsInsert_W(int cantParam, char **param);
static int MassiveAirDelete_W(int cantParam, char **param);
static int MassiveFlightDelete_W(int cantParam, char **param);
static int FindRoute_W(int cantParam, char **param);
static int RouteFormat_W(int cantParam, char **param);
static int RouteOutput_W(int cantParam, char **param);
static int DeleteFlight_W(int cantParam, char **param);
static int DeleteAirport_W(int cantParam, char **param);

/* Aplicacion fligthAssistant */
int
main(int argc, char *argv[])
{
	assistantADT assist = NULL;
	logADT nlog = NULL;
	commandADT command = NULL;

	nlog = NewLog("prueba.log");
	SetLogLevel(nlog, FATAL);
	assist = NewAssistant();

	if (argc > 1) /* Flujo por argumentos de linea de comandos del SO */
	{
		if ( !parseArgCommandLine(argc, argv) )
		{
			fprintf(stderr, "Comando invalido.\n");
			FreeAssistant();
			CloseLog(nlog);
			return 1;
		}
	}

	/* Se crean los comandos validos */
	if ( Precondicion( (command = NewCommand())) )
	{
		FreeAssistant();
		CloseLog(nlog);
		return 1;
	}

	shellCommand(command); /* Ingreso al shell de la aplicacion */
	FreeAssistant();
	FreeCommand(command);
	CloseLog(nlog);
	printf("\n");
	return 0;
}

static void
shellCommand(commandADT command)
{
	int sigoenshell=1, parseOk, i;
	char buffer[MAX_BUFFER];
	/* Nombre de los comandos validos de la aplicacion */
	char *cmdName[MAX_COMMAND] = { "FindRoute", "OutputFormat", "Output",
										  "Insert airport", "Insert flight",
										  "Delete airport", "Delete flight",
										  "Insert All airport", "Insert All flight",
										  "Delete All airport", "Delete All flight" };

	/* Cantidad de tokens de un comando */
	int cantCommandTokens[MAX_COMMAND] = {1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};

	/* Cantidad de tokens de los parametros de un comando */
	int cantParamTokens[MAX_COMMAND] = {4, 1, 1, 3, 8, 1, 2, 1, 1, 0, 0};

	int cantOptTokens[MAX_COMMAND] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

	/* Arreglo de punteros a funciones Wrappers */
	commandFn cmdFnW[MAX_COMMAND] = {FindRoute_W, RouteFormat_W, RouteOutput_W,
									 InsertAirport_W, InsertFlight_W, DeleteAirport_W,
									 DeleteFlight_W, MassiveAirInsert_W, MassiveFlightsInsert_W,
									 MassiveAirDelete_W, MassiveFlightDelete_W};

	printf("\nWelcome to flightAssistant ...\n\n");

	for (i=0; i<11; i++)
		if ( !InsertCommand(command, cmdName[i], cmdFnW[i], cantCommandTokens[i], cantParamTokens[i], cantOptTokens[i]) )
			return;

	while (sigoenshell)
	{
		SetBeginCommand(command);
		printf("flightAssistant> ");
				/* Prompt de la aplicacion */
		if ( fgets(buffer, MAX_BUFFER, stdin) != NULL )	/* se obtiene comando del usuario */
		{	
			buffer[strlen(buffer)-1] = '\0';
			
			if (!strcmp(buffer, "exit"))		/* Si ingresa exit o salir termina la aplicacion */
					sigoenshell = 0;
	
			else if (!strcmp(buffer, "salir"))
					sigoenshell = 0;
	
			else if (!strcmp(buffer, "help"))
			{
				printf("Add an airport: \"Insert airport Name Lat Lng\"\n");
				printf("Delete an airport: \"Delete airport Name\"\n");
				printf("Add airports from file: \"Insert All airport FILE\"\n");
				printf("Delete all airports: \"Delete All airport\"\n");
				printf("Add a flight: \"Insert flight Airline Flightnumber Weekdays Source Target DepartureTime FlightTime Price\"\n");
				printf("Add flights from file: \"Insert All flight FILE\"\n");
				printf("Delete a flight: \"Delete flight Airline Flightnumber\"\n");
				printf("Delete a flight: \"Delete All flight\"\n");
				printf("Find an optimal route: \"FindRoute src=srcairport dst=dstairport prio={ff|pr|tt} [weekdays=days]\"\n");
				printf("Exit program: \"exit\"\n");
				printf("\n");
			}
			/* Analisis de comando ingresado por el usuario */
			else
			{
				if ( (parseOk = parseCommand(command, buffer)) == -1 )
				{
					Fatal("main- not enough memory\n");
					return;
				}
	
				if (parseOk == 0)
					fprintf(stderr,"Comando Invalido\n");
			}
		}
	}
	return;
}

static int
parseArgCommandLine(int argc, char **argv)
{
	char *param[2] = {0};

	param[0] = malloc(sizeof(char) * MAX_LENGTH_PARAM);
	param[1] = malloc(sizeof(char) * MAX_LENGTH_PARAM);

	/* Carga masiva de aeropuertos */
	if ( !strcmp(argv[1], "--airport-file") && argc == 4 )
	{
		strcpy(param[0], argv[2]);

		if ( !strcmp(argv[3], "--append-airports") )
		{
			strcpy(param[1], "APPEND");
			MassiveAirInsert_W(2, param);
			return 1;
		}
		else if ( !strcmp(argv[3], "--replace-airports") )
		{
			strcpy(param[1], "REPLACE");
			MassiveAirInsert_W(2, param);
			return 1;
		}
	}

	/* Carga masiva de vuelos */
	else if ( !strcmp(argv[1], "--flight-file") && argc == 4 )
	{
		strcpy(param[0], argv[2]);

		if ( !strcmp(argv[3], "--append-flights") )
		{
			strcpy(param[1], "APPEND");
			MassiveFlightsInsert_W(2, param);
			return 1;
		}
		else if ( !strcmp(argv[3], "--replace-flights") )
		{
			strcpy(param[1], "REPLACE");
			MassiveFlightsInsert_W(2, param);
			return 1;
		}
	}

	/* Borrado masivo de aeropuertos */
	else if ( !strcmp(argv[1], "--delete-airports") && argc == 2)
	{
		MassiveAirDelete_W(0, NULL);
		return 1;
	}

	/* Borrado masivo de vuelos */
	else if ( !strcmp(argv[1], "--delete-flights") && argc == 2)
	{
		MassiveFlightDelete_W(0, NULL);
		return 1;
	}

	free(param[0]);
	free(param[1]);

	return 0;
}

/* Implementacion de funciones Wrappers */

static int
InsertAirport_W(int cantParam, char **param)
{
	parseFn	fnparse[AIRARGS] = {parseName, parseLatitude, parseLongitude};
	int i;

	for (i = 0 ; i < cantParam ; i++)
		if ( !fnparse[i]((const char *)param[i]) )
			return 0;

	if ( InsertAirport(param[0], atoi(param[1]), atoi(param[2]) ) == FN_ERROR)
	{
		printf("Airport %s not inserted\n", param[0]);
		return FN_ERROR;
	}

	printf("Airport %s succesfully inserted\n", param[0]);

	return 1;
}

static int
InsertFlight_W(int cantParam, char **param)
{
	parseFn	fnparse[FLIGHTARGS] = {parseName, parseFnum, parseDays, parseName, parseName,
								   parseDep, parseFtime, parsePrice};
	int i;

	for (i = 0 ; i < cantParam ; i++)
		if ( !fnparse[i]((const char *)param[i]) )
			return 0;

	if ( InsertFlight(param[0], atoi(param[1]), param[2], param[3], param[4],
					   param[5], param[6], atof(param[7])) == FN_ERROR)
	{
		printf("Flight %s from airport %s to %s not inserted\n", param[0], param[3], param[4]);
		return FN_ERROR;
	}

	printf("Flight %s from airport %s to %s succesfully inserted\n", param[0], param[3], param[4]);
	return 1;
}

static int
MassiveAirInsert_W(int cantParam, char **param)
{
	int mai;
	insT ins;

	if (cantParam == 1)
		mai = MassiveAirInsert(param[0], APPEND);

	else
	{
		if (strcmp(param[1], "APPEND") == 0 )
			ins = APPEND;
		else
			ins = REPLACE;

		mai = MassiveAirInsert(param[0], ins);
	}

	if ( mai == FN_ERROR || mai == 0 )
	{
		printf("Errors occured during Massive airport Insert\n");
		return FN_ERROR;
	}

	printf("All airports were inserted\n");
	return mai;
}

static int
MassiveFlightsInsert_W(int cantParam, char **param)
{
	int mfi;
	insT ins;

	if (cantParam == 1)
		mfi = MassiveFlightInsert(param[0], APPEND);
	else
	{
		if (strcmp(param[1], "APPEND") == 0 )
			ins = APPEND;
		else
			ins = REPLACE;

		mfi = MassiveFlightInsert(param[0], ins);
	}

	if ( mfi == FN_ERROR || mfi == 0)
	{
		printf("Errors ocurred during flight insertions\n");
		return FN_ERROR;
	}

	printf("All flights were inserted\n");
	return mfi;
}

static int
MassiveAirDelete_W(int cantParam, char **param)
{
	int madok;

	if ( (madok = MassiveAirDelete()) == FN_ERROR )
	{
		printf("Massive Air Delete failed\n");
		return FN_ERROR;
	}
	printf("All airports were deleted\n");
	return madok;
}

static int
MassiveFlightDelete_W(int cantParam, char **param)
{
	int mfdok;

	if ( (mfdok = MassiveFlightDelete()) == FN_ERROR)
	{
		printf("Massive Flight Delete failed\n");
		return FN_ERROR;
	}
	printf("All flights have been deleted\n");
	return mfdok;
}

static int
FindRoute_W(int cantParam, char **param)
{
	int fok;
	prioT prio = PR;
	parseFn fnparse[4] = {parseName, parseName, parsePrio, parseDays};
	char * aux[4] = {0};
	char * directive[4] = {"src", "dst", "prio", "weekdays"};
	int i;

	/* destruye por ejemplo src= , del string */
	for (i = 0 ; i < cantParam ; i++)
	{
		if (param[i] != NULL)
		{
			if ( strcmp(strtok(param[i], "=\0"), directive[i]) == 0 )
				aux[i] = strtok(NULL, "=\0");
			else
				return 0;
		}
		
	}

	for (i = 0 ; i < cantParam ; i++)
	{
		/* Cuando se parsea la prioridad, tambien se identifica de que tipo es */
		if (i == 2)
		{
			if ( (prio = (prioT)fnparse[i](aux[i])) == 0)
				return 0;
		}
		else
		{
			if (aux[i] != NULL &&  fnparse[i](aux[i]) == 0)
				return 0;
		}
	}


	fok = FindRoute(aux[0], aux[1], prio, aux[3]);

	switch(fok)
	{
		case 1:
						fok = 1;
						break;
		case PATHERROR:
						printf("NotFound\n");
						fok = FN_ERROR;
						break;
		case FN_ERROR:
						printf("NotFound\n");
						fok = FN_ERROR;
						break;
		default:
						fok = 1;
						break;
	}

	return fok;
}


static int
RouteOutput_W(int cantParam, char ** param)
{
	FILE * outfile;

	if (strcmp(param[0], "Stdout") == 0 )
		return RouteOutput(stdout);

	if (!isalnum(*param[0]))
		return 0;

	if ( (outfile = fopen(param[0], "w+t")) == NULL )
	{
		Error("main - could not create file %s\n", param[0]);
		return FN_ERROR;
	}

	return RouteOutput(outfile);
}

static int
RouteFormat_W(int cantParam, char **param)
{
	fmtT fmt;

	if ( strcmp(param[0], "Text") == 0 )
		fmt = PLAIN;
	else if ( strcmp(param[0], "KML") == 0 )
		fmt = KML;
	else
		return FN_ERROR;

	return RouteFormat(fmt);
}

static int
DeleteAirport_W(int cantParam, char **param)
{
	int dok;

	if ( !parseName(param[0]) )
		return 0;

	dok = DeleteAirport(param[0]);

	printf("Airport %s %s deleted\n", param[0], dok == 1? "" : "not");
	return dok;
}

static int
DeleteFlight_W(int cantParam, char **param)
{
	int dok;
	if ( !parseName(param[0]) || !parseFnum(param[1]) )
		return 0;

	dok = DeleteFlight(param[0], atoi(param[1]));
	printf("Flight %s %s deleted\n", param[0], dok == 1? "" : "not");
	return dok;
}
